LIBRERIAS

CARGA DE BASES

Nueva Corrección duplicados

df_audio_features <- df_audio_features_raw %>% 
  group_by(track_name, external_urls_spotify) %>% 
  mutate(artist_all = paste(artist_name, collapse = ",|,")) %>%
  ungroup() %>% 
  mutate(artist_key = sub(",|,.*", "", artist_all)) %>% 
  dplyr::select(artist_name, artist_all, artist_key, everything(.)) %>% 
  distinct(artist_key, external_urls_spotify, .keep_all = T) %>% 
  as.data.frame()
Error in df_audio_features_raw %>% group_by(track_name, external_urls_spotify) %>%  : 
  could not find function "%>%"

CREACION cant_markets

Charts

VECTORES DE FEATURES

RIGTH JOIN audio_features Y charts

#Armamos un join para tener una tabla de charts con las caracteristicas de las canciones
# deberian quedar 22993 filas completas
join_audio_charts <- df_audio_features %>% 
  select("artist_name","artist_all","artist_key",
         "track_name", "external_urls_spotify", "album_name", "album_release_year",
         all_of(features_continuas), all_of(features_categoricas)) %>% 
  right_join( df_charts,# %>%
               by = c(
                 "track_name" = "Track_Name", 
                      "artist_key" ="Artist", 
                      "external_urls_spotify" = "URL"))

#HAY CHARTS QUE NO TIENEN FEATURES. HAY QUE TENERLO EN CUENTA PARA EL ANÁLISIS
library(mice)
md.pattern(join_audio_charts, rotate.names = TRUE)
popularidad[is.na(popularidad$indicador),]

#Agregación de todas las semanas en charts

HISTOGRAMAS Y BARPLOTS DE VARIABLES


##histograma de las variables continuas de audio_features

for (i in features_continuas){

  hist(df_audio_features[,i], main = paste("Histograma de", i, "(all data)"), xlab = i)
  abline(v = mean(df_audio_features[,i], na.rm = TRUE) , col="red")
  abline(v = median(df_audio_features[,i], na.rm = TRUE) , col="blue")
  legend("topright", legend = c("Media", "Mediana"), col=c("red", "blue"), lty =1)

}

#divido los features por su distribución
features_continuas_media <- c('danceability', 'tempo', 'valence')

features_continuas_mediana <- c('acousticness', 'duration_ms', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets')


##histograma de las variables continuas de charts
for (i in c(features_continuas, "Streams")){

  hist(join_audio_charts[,i], main = paste("Histograma de", i,  "(charts)"), xlab = i)
  abline(v = mean(join_audio_charts[,i], na.rm = TRUE) , col="red")
  abline(v = median(join_audio_charts[,i], na.rm = TRUE) , col="blue")

}

#divido features de charts según su distribución
audio_charts_continuas_media <- c('duration_ms', 'valence')

audio_charts_continuas_mediana <- c('danceability', 'acousticness', 'tempo', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets', "Streams")


##medidas resumen y barplots de las variables categoricas audio_features
for(i in features_categoricas){

  barplot(sort(table(df_audio_features[,i]),decreasing = T), las=2, 
          main = paste("Barplot de", i, "(all data)"))
  # pie(table(df_features_categoricos[,i]))
}



##medidas resumen y barplots de las variables categoricas join_audio_charts

for(i in features_categoricas){
  
  barplot(sort(table(join_audio_charts[,i]),decreasing = T), las=2, 
          main = paste("Barplot de", i, "(charts)"))
  # pie(table(df_features_categoricos[,i]))
}

Analisis de la variable markets_concat

#Hago un join al revés 

df_chart_tojoin <- df_charts[,c("Track_Name", "Artist", "URL")]
df_chart_tojoin$isinchart <- 1
df_audio_features_tojoin <- df_audio_features[, c("track_name","artist_key","external_urls_spotify","markets_concat")]

join_barplot <- df_audio_features_tojoin %>% 
  select("track_name","artist_key","external_urls_spotify","markets_concat") %>% 
  left_join( df_chart_tojoin %>%
               select("Track_Name", "Artist", "URL","isinchart"),
               by = c(
                 "track_name" = "Track_Name", 
                      "artist_key" ="Artist", 
                      "external_urls_spotify" = "URL"))


join_barplot$isinchart[is.na(join_barplot$isinchart)] <- 0

join_barplot$isinchart <- factor(join_barplot$isinchart)

tabla_isinchart <- table(unlist(lapply(join_barplot[join_barplot$isinchart==1,"markets_concat"], function(x) strsplit(as.character(x), ','))))

tabla_notinchart <- table(unlist(lapply(join_barplot[join_barplot$isinchart==0,"markets_concat"], function(x) strsplit(as.character(x), ','))))

all_countries <- names(tabla_isinchart)

xlabs <- paste(paste(head(all_countries,3), collapse = ","),"...",paste(tail(all_countries,3),collapse = ","),"(ISO-Codes de Paises)",  collapse = ",")

options(scipen=999)
par(mfrow = c(1,2), las=1, mar=c(3,3,5,3), oma=c(0,1,1,1))
barplot(sort(tabla_isinchart, decreasing = TRUE), names.arg="", main ='En Charts',col=rgb(0.2,0.4,0.6,0.6),xlab = "Paises (ISO-Codes)")
# mtext(side = 1, text = xlabs, line = 1)
barplot(sort(tabla_notinchart, decreasing = TRUE), names.arg = "", main='Fuera de Charts',col=rgb(0.2,0.4,0.6,0.6), xlab = "Paises (ISO-Codes)")
mtext(side = 1, text = xlabs, line = 1, adj = 2)
mtext("Frecuencia de mercados habilitados", side = 3, line = -1, outer = TRUE, cex = 1.3, font =2 )
# mtext("Paises (ISO-Codes)", side = 3, line = -25, outer = TRUE)

CORRELACIONES

#correlaciones en audio features
x <- cor(df_audio_features[,c(features_continuas_media, features_continuas_mediana)],  use =  "complete.obs")
corrplot(x, type = "upper", title = "Correlacion de atributos de audio_features", mar=c(0,0,1,0), method="number" ,number.cex=0.7)

#correlaciones en charts
x <- cor(scale(join_audio_charts[,c(audio_charts_continuas_media, audio_charts_continuas_mediana)]), use =  "complete.obs")
corrplot(x, type = "upper", title = "Correlacion de atributos de los Charts", mar=c(0,0,1,0), method="number", number.cex=0.7 )

#chi2 test #con n grande no se puede usar este test
tabla_key_album <- table(df_audio_features$key_name, df_audio_features$album_type)
cat("Tabla de contigencia entre key y album type\n")
tabla_key_album
chisq.test(tabla_key_album)

SESGO DE VARIABLES

Boxplots Variables Numéricas sin filtrar outliers

#divido los features por su distribución
features_continuas_media <- c('danceability', 'tempo', 'valence')

features_continuas_mediana <- c('acousticness', 'duration_ms', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets')

all_features <- c(features_continuas_media, features_continuas_mediana)

par(mfrow=c(4,3))
for (feature in all_features){
  boxplot(df_audio_features[,feature], las=2, horizontal=T, main=feature)
}

Con excepción de valence el resto de las features poseían cierto sesgo. Se decidió transformar las variables que mayor sesgo poseían: duration_ms, instrumentalness, liveness, speechiness como método de corregir la distribución y achicar la cantidad de outliers. La variable loudness_reg_imp no fue modificada debido a que al ser negativa

# "danceability,tempo,valence,acousticness,duration_ms,energy,instrumentalness,liveness,speechiness,cant_markets"

#sesgos d las variables                                                   
sort(apply(df_audio_features[,features_continuas], MARGIN = 2, function(x){ (3* (mean(x,na.rm = T)-median(x, na.rm = T)))/sd(x, na.rm = T)} ))

variables_sesgo <- unlist(strsplit("acousticness,duration_ms,instrumentalness,liveness,speechiness,cant_markets,energy", ","))

df_sesgadas <- df_audio_features[,variables_sesgo]

logaritmo_ajustado = function(x,delta){
  if (x==0.0){
    return(log(0.00+delta, base = 10))
  }else{
    return(log(x, base = 10))
  }
}

delta <- 10^(-6)

df_sesgadas_log_adjust <- data.frame(apply(df_audio_features[,variables_sesgo], MARGIN = c(1,2), 
                                           function(x) logaritmo_ajustado(x,delta)))
# names(df_sesgadas_log_adjust) <- paste(names(df_sesgadas), "_log", sep="")
names(df_sesgadas_log_adjust) <- names(df_sesgadas)

df_datos <- cbind(df_sesgadas, df_sesgadas_log_adjust)



a <- df_sesgadas
b <- df_sesgadas_log_adjust
names(b) <- paste(names(df_sesgadas), "_log", sep="")
merged <- cbind(a,b)

merged <- merged[, order(names(merged))]

round(sort(apply(merged, MARGIN = 2, function(x){ (3* (mean(x,na.rm = T)-median(x, na.rm = T)))/sd(x, na.rm = T)})),2)

variables_plot <- unlist(strsplit("duration_ms", ","))
variables_plot <- append(variables_plot,paste(variables_plot,"_log", sep=""))
variables_plot <- variables_plot[order(variables_plot)]
plotear <- merged[,variables_plot]

par(mfrow = c(1,2))
for (col in names(plotear)){
  hist(plotear[,col], breaks="FD", main=col, xlab="")
}
summary(df_audio_features[,all_features])


hist(log(df_audio_features$duration_ms))

transformacion <- c('instrumentalness','loudness','liveness','speechiness', 'duration_ms')

logaritmo_ajustado = function(x,delta){
  if (x<=0.0){
    return(log(0.00+delta, base = 10))
  }else{
    return(log(x, base = 10))
  }
}

delta <- 10^(-6)

par(mfrow=c(2,4))
for (feature in transformacion){
  hist(df_audio_features[,feature], main=feature)
}
for (feature in transformacion){
  hist(unlist(lapply(df_audio_features[,feature], function(x) logaritmo_ajustado(x,delta))), main=paste(feature,"log", sep="_"))
}

inv_sqrt_ajustada = function(x, delta){
  if (x==0.0){
    return(1/sqrt(x+delta))
  }else{
    return(1/sqrt(x))
  }
}


delta <- 10^(-6)

par(mfrow=c(2,4))
for (feature in transformacion){
  hist(df_audio_features[,feature], main=feature)
}
for (feature in transformacion){
  hist(unlist(lapply(df_audio_features[,feature], function(x) inv_sqrt_ajustada(x,delta))), main=paste(feature,"inv_sqt", sep="_"))
}


par(mfrow=c(2,4))
for (feature in transformacion){
  hist(df_audio_features[,feature], main=feature)
}
for (feature in transformacion){
  hist(sqrt(df_audio_features[,feature]), main=paste(feature,"sqrt", sep="_"))
}

par(mfrow = c(2,1)) 
hist(df_audio_features[,'loudness_reg_imp'], main='loudness', xlab="")
#hist(sqrt(df_audio_features[,'loudness_reg_imp']), main= 'loudness_sqrt', xlab="")
boxplot(df_audio_features[,'loudness_reg_imp'], horizontal = T)
#boxplot(sqrt(df_audio_features[,'loudness_reg_imp']), horizontal = T)
fit <- lm(loudness~energy+acousticness, data=df_audio_features)

modelo <- fit$coefficients

df_audio_features$loudness_reg_imp <- df_audio_features$loudness

X <- df_audio_features[df_audio_features$loudness>0, c('energy', "acousticness")]

df_audio_features$loudness_reg_imp[df_audio_features$loudness>0] <- modelo[1]+modelo[2]*X[,1]+modelo[3]*X[,2]

summary(df_audio_features[,c("loudness", "loudness_reg_imp")])

summary(fit)

instrumentalness tiene mucho sesgo la variable. Se va a recurrir a una logaritmización de la variable, previa transformación del dominio, haciendo que los valores que son 0, sean en realidad 0.0000001

logaritmo_ajustado = function(x,delta){
  if (x==0.0){
    return(log(x+delta, base = 10))
  }else{
    return(log(x, base = 10))
  }
}

delta <- 10^(-6)

df_audio_features$instrumentalness_logadjust <- unlist(lapply(df_audio_features$instrumentalness, function(x) logaritmo_ajustado(x,delta)))

par(mfrow =c(2,2))
hist(df_audio_features$instrumentalness, main="insrumentalness", xlab="")
hist(unlist(lapply(df_audio_features$instrumentalness, function(x) logaritmo_ajustado(x,delta))), main='instrumentalness_logadjust', ylim = c(0,130500), xlab = "")
boxplot(df_audio_features$instrumentalness, main="", horizontal = T)
boxplot(unlist(lapply(df_audio_features$instrumentalness, function(x) logaritmo_ajustado(x,delta))), main="", horizontal=T)
# hist(log(1/sqrt(df_audio_features$instrumentalness+0.00001)),main='log(sqrt(x+))', ylim=c(0,130500), xlab = "")

¿Es útil esta transformación?


delta <- 10^(-6)

df_audio_features$instrumentalness_logadjust <- unlist(lapply(df_audio_features$instrumentalness, function(x) logaritmo_ajustado(x,delta)))

df_chart_tojoin <- df_charts[,c("Track_Name", "Artist", "URL")]
df_chart_tojoin$isinchart <- 1
df_audio_features_tojoin <- df_audio_features[, c("track_name","artist_key","external_urls_spotify","instrumentalness", "instrumentalness_logadjust")]

join_histogram <- df_audio_features_tojoin %>% 
  dplyr::select("track_name","artist_key","external_urls_spotify","instrumentalness", "instrumentalness_logadjust") %>% 
  left_join( df_chart_tojoin %>%
               select("Track_Name", "Artist", "URL","isinchart"),
               by = c(
                 "track_name" = "Track_Name", 
                      "artist_key" ="Artist", 
                      "external_urls_spotify" = "URL"))


join_histogram$isinchart[is.na(join_histogram$isinchart)] <- 0

join_histogram$isinchart <- factor(join_histogram$isinchart)


h11 <- hist(join_histogram[join_histogram$isinchart==1,'instrumentalness'])
h11$density <-  h11$counts/sum(h11$counts)*100

h12 <- hist(join_histogram[join_histogram$isinchart==0,'instrumentalness'])
h12$density <-  h12$counts/sum(h12$counts)*100

h21 <- hist(join_histogram[join_histogram$isinchart==1,'instrumentalness_logadjust'])
h21$density <-  h21$counts/sum(h21$counts)*100

h22 <- hist(join_histogram[join_histogram$isinchart==0,'instrumentalness_logadjust'])
h22$density <-  h22$counts/sum(h22$counts)*100

#png("C:/Users/Asus/Desktop/DATA SCIENCE/MAESTRIA/Data Mining/TP/graficos/instrumentalness.png",
#    width = 800, height = 800)
par(mfrow = c(3,2))
plot(h11, main='instrumentalness \nchart', xlab="", ylab="Porcentage", freq=FALSE, col='grey', ylim = c(0,100))
plot(h12, main='instrumentalness \nfuera chart', xlab="", ylab="Porcentage", freq=FALSE, col='grey', ylim = c(0,100))
plot(h21, main ="instrumentalness_log \nchart", xlab="", ylab="Porcentage", freq=FALSE, col='grey', ylim = c(0,100))
plot(h22, main ="instrumentalness_log \nfuera chart", xlab="", ylab="Porcentage", freq=FALSE, col='grey', ylim = c(0,100))
boxplot(join_histogram[join_histogram$isinchart==1,'instrumentalness_logadjust'], main="instrumentalness_log chart", horizontal = T)
boxplot(join_histogram[join_histogram$isinchart==0,'instrumentalness_logadjust'], main="instrumentalness_log fuera chart", horizontal = T)
#dev.off()

Z-Score de Variables que “tienden a la normal”


################################

## FILTRAMOS OUTLIERS POR Z-SCORE para 'danceability', 'tempo', 'valence'

##############################

#z-score para variables que tienden a la normal
#filtro features numericos 

#divido los features por su distribución
features_continuas_media <- c('danceability', 'tempo', 'valence')
df_audio_features_zscore_media <- df_audio_features[,features_continuas_media]

#normalizo z score con las variables que tienden a la normal

zscore_cols <- c()
for(col in names(df_audio_features_zscore_media)){
  name_col <- paste('zscore_',col, sep = "")
  zscore_cols <- append(zscore_cols, name_col)
  media <-  mean(df_audio_features_zscore_media[,col])
  stdv <- sd(df_audio_features_zscore_media[,col])
  df_audio_features_zscore_media[,name_col] <- (df_audio_features_zscore_media[,col] - media)/stdv
  }

par(mfrow=c(1,length(zscore_cols)))
lapply(zscore_cols, function(col) boxplot(df_audio_features_zscore_media[,col],xlab=col))

Analisis de Z-Score por variable

Danceability

#variable: danceability

umbral_zscore <- 3
conditions <- (df_audio_features_zscore_media$zscore_danceability> umbral_zscore) | (df_audio_features_zscore_media$zscore_danceability< -1*umbral_zscore)
df_audio_features[conditions,] %>%
  select(album_name,artist_name, danceability ) %>%
  arrange(-danceability)

Tempo

#variable: Tempo

umbral_zscore <- 3
conditions <- (df_audio_features_zscore_media$zscore_tempo> umbral_zscore) | (df_audio_features_zscore_media$zscore_tempo< -1*umbral_zscore)
df_audio_features[conditions,] %>%
  select(album_name,artist_name, tempo ) %>%
  arrange(-tempo)

Valence

#variable: valence
umbral_zscore <- 3
conditions <- (df_audio_features_zscore_media$zscore_valence> umbral_zscore) | (df_audio_features_zscore_media$zscore_valence< -1*umbral_zscore)
df_audio_features[conditions,] %>%
  select(album_name,artist_name, valence ) %>%
  arrange(-valence)

Z-Score Modificado de Variables Asimetricas

################################

## FILTRAMOS OUTLIERS POR Z-SCORE MODIFICADO para 'acousticness', 'duration_ms', 'energy',  'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets'

##############################

features_continuas_mediana <- c('acousticness', 'duration_ms', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets')

df_audio_features_zscore_mediana <- df_audio_features[,features_continuas_mediana]



zscoremodif_cols <- c()
for(col in names(df_audio_features_zscore_mediana)){
  name_col <- paste('zscoremodif_',col, sep = "")
  zscoremodif_cols <- append(zscoremodif_cols, name_col)
  med = median(df_audio_features_zscore_mediana[,col], na.rm = T)
  MAD = median(abs(df_audio_features_zscore_mediana[,col] - med), na.rm = T)
  df_audio_features_zscore_mediana[, name_col] <- 0.6745 * (df_audio_features_zscore_mediana[,col] - med) / MAD
}


par(mfrow=c(4,2))
lapply(zscoremodif_cols, function(col) boxplot(df_audio_features_zscore_mediana[,col],xlab=col, horizontal = T))

Revisión Variable Instrumentalness

instrumentalness <- c("instrumentalness", "zscoremodif_instrumentalness") 

x <- df_audio_features$instrumentalness

n_interv <- 10


intervalos <- round(seq(0,max(x),by=(max(x)-min(x))/n_interv),2)

labs <- c()
for (i in 1:n_interv){
lab <- paste(intervalos[i],intervalos[i+1], sep='\n')
labs <- append(labs, lab)
    
}

bins <- cut(x, n_interv, include.lowest = TRUE, labels = labs)

barplot(table(bins))

Hacemos K-means para poder discretizar la variable.

sse <- c()
for (k in 2:6){
  clusters <- kmeans(df_audio_features$instrumentalness,centers = k, iter.max = 10, nstart = k)
  sse <- append(sse, clusters$tot.withinss)
}

plot(2:6,sse, type = 'l', xlab='Cantidad de Clusters', ylab='Suma Error Cuadrático')

#k=3 
clusters3 <- kmeans(df_audio_features$instrumentalness,centers = 3, iter.max = 10, nstart = 3)

df_audio_features$clusters <- factor(clusters3$cluster)

lev <- levels(df_audio_features$clusters)

labs <- c()
for (i in lev){
  min <- min(df_audio_features$instrumentalness[df_audio_features$clusters==i])
  max <- max(df_audio_features$instrumentalness[df_audio_features$clusters==i])
  lab <- paste(min,max, sep=' - ')
  labs <- append(labs, lab)
}

labs

# barplot(table(factor(clusters3$cluster)), labels = labs)

Preguntas de investigacion

Patron Comun Canciones del Chart

¿Qué características tienen las canciones que están en el chart? ¿Cual es el patrón comun que tienen las canciones más escuchadas? (ver dispersiones, media, grafico comparativo)



#funcion para escalar variable
scale_vble <- function(x){
  (x - mean(x, na.rm = T))/sd(x, na.rm = T)
}
#anti_join
anti_join_audio_charts <- df_audio_features %>% 
  select("artist_name","artist_all", "artist_key",
         "track_name", "external_urls_spotify", "album_name", "album_release_year",
         all_of(features_continuas), all_of(features_categoricas)) %>% 
  anti_join( df_charts %>%
               select( "Track_Name", "Artist", "URL"),
               by = c("external_urls_spotify" ="URL",
                      "artist_key" ="Artist"  ))
               # by = c("track_name" = "Track_Name"))


anti_join_audio_charts_complete <- na.omit(anti_join_audio_charts)
anti_join_audio_charts_complete_scale <- anti_join_audio_charts_complete %>% 
  distinct() %>% 
  select(features_continuas)  %>% 
  mutate_all(scale_vble)
nrow(anti_join_audio_charts_complete_scale)

Qué temas perduran mucho en el ranking

Artistas que mas aparecen en el chart

join_audio_charts %>% 
  group_by(artist_name) %>% 
  dplyr::summarise(n = n()) %>% 
  arrange(-n)

Tracks que mas aparecen en el chart

join_audio_charts %>% 
  group_by(track_name, artist_name,external_urls_spotify) %>% 
  dplyr::summarise(n = n()) %>% 
  arrange(-n) %>% 
  select(track_name, n, everything(.))

¿Cuánto tiempo están en un chart?

# cantidad de semanas que estuvieron en el chart

df_charts %>% 
  mutate(week_start=as.Date(week_start),
         week_end = as.Date(week_end),
         week_year = (year(week_start))) %>%
  arrange(Artist, Track_Name) %>% 
  group_by(Artist, Track_Name, URL) %>% 
 dplyr:: summarise( day_in = min(week_start),
             year_in = year(day_in),
             day_max = max(week_end),
             year_max = year(day_max),
             duracion_chart_dias = day_max-day_in,
             duracion_chart_anio = year_max - year_in) %>% 
  arrange(Artist)

#prueba igal de transformacion y test de normalidad

for (i in features_continuas){
   x <- log10(df_chart_w_lyrics[,i])
   x <- shapiro.test(x)
   z <- x$p.value
  print(z)
  }
[1] 1.85241e-21
[1] 1.167246e-23
[1] 7.44622e-11
[1] 1.256851e-30
[1] NaN
[1] 3.287572e-17
NaNs producedError in shapiro.test(x) : sample size must be between 3 and 5000
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIExJQlJFUklBUw0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShzcWxkZikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQojIENBUkdBIERFIEJBU0VTIA0KYGBge3J9DQpkZl9hcnRpc3QgPC0gcmVhZC5jc3YoImRhdGEvZGZfYXJ0aXN0X3Npbl9kdXBsaWNhZG9zLmNzdiIpDQpkZl9jaGFydHNfcmF3IDwtIHJlYWQuY3N2KCJkYXRhL2RmX2NoYXJ0c19zaW5fZHVwbGljYWRvcy5jc3YiKQ0KZGZfYXVkaW9fZmVhdHVyZXNfcmF3IDwtIHJlYWQuY3N2KCJkYXRhL2F1ZGlvX2ZlYXR1cmVzX3BsYW5vX3Npbl9kdXBsaWNhZG9zLmNzdiIpDQpkZl9seXJpY3MgPC0gcmVhZC5jc3YoImRhdGEvZGZfbHlyaWNzLmNzdiIpDQoNCmBgYA0KDQoNCiMjIE51ZXZhIENvcnJlY2Npw7NuIGR1cGxpY2Fkb3MNCmBgYHtyfQ0KIyBERiBsaXN0byBwYXJhIGVsIGpvaW4gY29uIGNocmF0cw0KZGZfYXVkaW9fZmVhdHVyZXMgPC0gZGZfYXVkaW9fZmVhdHVyZXNfcmF3ICU+JSANCiAgZ3JvdXBfYnkodHJhY2tfbmFtZSwgZXh0ZXJuYWxfdXJsc19zcG90aWZ5KSAlPiUgDQogIG11dGF0ZShhcnRpc3RfYWxsID0gcGFzdGUoYXJ0aXN0X25hbWUsIGNvbGxhcHNlID0gIix8LCIpKSAlPiUNCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGFydGlzdF9rZXkgPSBzdWIoIix8LC4qIiwgIiIsIGFydGlzdF9hbGwpKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoYXJ0aXN0X25hbWUsIGFydGlzdF9hbGwsIGFydGlzdF9rZXksIGV2ZXJ5dGhpbmcoLikpICU+JSANCiAgZGlzdGluY3QoYXJ0aXN0X2tleSwgZXh0ZXJuYWxfdXJsc19zcG90aWZ5LCAua2VlcF9hbGwgPSBUKSAlPiUgDQogIGFzLmRhdGEuZnJhbWUoKQ0KYGBgDQoNCiMgQ1JFQUNJT04gYGNhbnRfbWFya2V0c2ANCmBgYHtyfQ0KY29udGFyX21hcmtldCA8LSBmdW5jdGlvbih4KXsNCnEgPC0gbGVuZ3RoKHVubGlzdChzdHJzcGxpdCh4LCBzcGxpdCA9ICIsIikpKQ0KcmV0dXJuIChxKQ0KICB9DQpkZl9hdWRpb19mZWF0dXJlcyRjYW50X21hcmtldHMgPC0gc2FwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzWywibWFya2V0c19jb25jYXQiXSwgY29udGFyX21hcmtldCkNCmBgYA0KDQojIENoYXJ0cw0KYGBge3J9DQojbWV0cmljYSBkZSBwb3B1bGFyaWRhZA0KZGZfY2hhcnRzIDwtIGRmX2NoYXJ0c19yYXcgJT4lIA0KICBncm91cF9ieShBcnRpc3QsIFRyYWNrX05hbWUsIFVSTCkgJT4lDQogIGRwbHlyOjogc3VtbWFyaXNlKHNlbWFuYXNfc3VtID0gbigpLA0KICAgICAgICAgICAgc3RyZWFtc19zdW0gPSAoc3VtKFN0cmVhbXMsIG5hLnJtID0gVCkvMTBeNiApLA0KICAgICAgICAgICAgc3RyZWFtc19taW4gPSAobWluKFN0cmVhbXMpLzEwXjYgKSwNCiAgICAgICAgICAgIHN0cmVhbXNfbWF4ID0gKG1heChTdHJlYW1zKS8xMF42ICksDQogICAgICAgICAgICBwb3NpdGlvbl9hdmcgPSBtZWFuKFBvc2l0aW9uLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgcG9zaXRpb25fbWluID0gbWluKFBvc2l0aW9uKSwgDQogICAgICAgICAgICBwb3NpdGlvbl9tYXggPSBtYXgoUG9zaXRpb24pKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShpbmRpY2Fkb3IgPSBhcy5udW1lcmljKHN0cmVhbXNfc3VtKnNlbWFuYXNfc3VtL3Bvc2l0aW9uX2F2ZykgKQ0KDQpgYGANCg0KDQojIFZFQ1RPUkVTIERFIEZFQVRVUkVTDQpgYGB7cn0NCiNmZWF0dXJlcyB2YXIgY29udGludW9zDQpmZWF0dXJlc19jb250aW51YXMgPC0gYygnYWNvdXN0aWNuZXNzJywgJ2RhbmNlYWJpbGl0eScsICdkdXJhdGlvbl9tcycsICdlbmVyZ3knLCAnaW5zdHJ1bWVudGFsbmVzcycsICdsaXZlbmVzcycsICdsb3VkbmVzcycsICdzcGVlY2hpbmVzcycsICAgJ3RlbXBvJywgJ3ZhbGVuY2UnLCAnY2FudF9tYXJrZXRzJykNCg0KI2ZlYXR1cmVzIHZhcl8gY2F0ZWfDs3JpY2FzDQpmZWF0dXJlc19jYXRlZ29yaWNhcyA8LSBjKCdleHBsaWNpdCcsICdrZXlfbmFtZScsICdtb2RlX25hbWUnLCAia2V5X21vZGUiLCAiYWxidW1fdHlwZSIpDQoNCmBgYA0KDQoNCg0KIyBSSUdUSCBKT0lOIGBhdWRpb19mZWF0dXJlc2AgWSBgY2hhcnRzYA0KYGBge3J9DQojQXJtYW1vcyB1biBqb2luIHBhcmEgdGVuZXIgdW5hIHRhYmxhIGRlIGNoYXJ0cyBjb24gbGFzIGNhcmFjdGVyaXN0aWNhcyBkZSBsYXMgY2FuY2lvbmVzDQojIGRlYmVyaWFuIHF1ZWRhciAyMjk5MyBmaWxhcyBjb21wbGV0YXMNCmpvaW5fYXVkaW9fY2hhcnRzIDwtIGRmX2F1ZGlvX2ZlYXR1cmVzICU+JSANCiAgc2VsZWN0KCJhcnRpc3RfbmFtZSIsImFydGlzdF9hbGwiLCJhcnRpc3Rfa2V5IiwNCiAgICAgICAgICJ0cmFja19uYW1lIiwgImV4dGVybmFsX3VybHNfc3BvdGlmeSIsICJhbGJ1bV9uYW1lIiwgImFsYnVtX3JlbGVhc2VfeWVhciIsDQogICAgICAgICBhbGxfb2YoZmVhdHVyZXNfY29udGludWFzKSwgYWxsX29mKGZlYXR1cmVzX2NhdGVnb3JpY2FzKSkgJT4lIA0KICByaWdodF9qb2luKCBkZl9jaGFydHMsIyAlPiUNCiAgICAgICAgICAgICAgIGJ5ID0gYygNCiAgICAgICAgICAgICAgICAgInRyYWNrX25hbWUiID0gIlRyYWNrX05hbWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAiYXJ0aXN0X2tleSIgPSJBcnRpc3QiLCANCiAgICAgICAgICAgICAgICAgICAgICAiZXh0ZXJuYWxfdXJsc19zcG90aWZ5IiA9ICJVUkwiKSkNCg0KI0hBWSBDSEFSVFMgUVVFIE5PIFRJRU5FTiBGRUFUVVJFUy4gSEFZIFFVRSBURU5FUkxPIEVOIENVRU5UQSBQQVJBIEVMIEFOw4FMSVNJUw0KbGlicmFyeShtaWNlKQ0KbWQucGF0dGVybihqb2luX2F1ZGlvX2NoYXJ0cywgcm90YXRlLm5hbWVzID0gVFJVRSkNCnBvcHVsYXJpZGFkW2lzLm5hKHBvcHVsYXJpZGFkJGluZGljYWRvciksXQ0KDQpgYGANCg0KDQoNCiNBZ3JlZ2FjacOzbiBkZSB0b2RhcyBsYXMgc2VtYW5hcyBlbiBjaGFydHMNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KDQpmZWF0dXJlc19jb250aW51YXMgPC0gYygnYWNvdXN0aWNuZXNzJywgJ2RhbmNlYWJpbGl0eScsICdkdXJhdGlvbl9tcycsICdlbmVyZ3knLCAnaW5zdHJ1bWVudGFsbmVzcycsICdsaXZlbmVzcycsICdsb3VkbmVzcycsICdzcGVlY2hpbmVzcycsICAgJ3RlbXBvJywgJ3ZhbGVuY2UnLCAnY2FudF9tYXJrZXRzJykNCg0KZmVhdHVyZXNfY2F0ZWdvcmljYXMgPC0gYygnZXhwbGljaXQnLCAna2V5X25hbWUnLCAnbW9kZV9uYW1lJywgImtleV9tb2RlIiwgImFsYnVtX3R5cGUiKQ0KDQpncm91cHBpbmdfY29scyA8LSBjKCJhcnRpc3RfbmFtZSIsImFydGlzdF9hbGwiLCJhcnRpc3Rfa2V5IiwidHJhY2tfbmFtZSIsImV4dGVybmFsX3VybHNfc3BvdGlmeSIsImFsYnVtX25hbWUiLCJhbGJ1bV9yZWxlYXNlX3llYXIiKQ0KDQpudW1lcmljX2NvbF9jaGFydHMgPC0gYygiUG9zaXRpb24iLCJTdHJlYW1zIikNCg0Kd2Vla19zdGFydCA8LSBjKCJ3ZWVrX3N0YXJ0IikNCg0KY2hhcnRfZ3JvdXAgPC0gam9pbl9hdWRpb19jaGFydHMgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KGFydGlzdF9uYW1lLGFydGlzdF9hbGwsYXJ0aXN0X2tleSx0cmFja19uYW1lLGV4dGVybmFsX3VybHNfc3BvdGlmeSxhbGJ1bV9uYW1lLGFsYnVtX3JlbGVhc2VfeWVhcikNCg0KDQpjb250aW51YXNfc3VtbWFyaXplZCA9IGNoYXJ0X2dyb3VwICU+JSBzdW1tYXJpc2VfYXQoZmVhdHVyZXNfY29udGludWFzLCBtZWFuLCBuYS5ybSA9IFRSVUUpDQpjYXRlZ29yaWNhc19zdW1tYXJpemVzID0gY2hhcnRfZ3JvdXAgJT4lIHN1bW1hcmlzZV9hdChmZWF0dXJlc19jYXRlZ29yaWNhcywgZmlyc3QpDQpudW1lcmljX2NoYXJ0c19zdW1tYXJpemVzID0gY2hhcnRfZ3JvdXAgJT4lIHN1bW1hcmlzZShhY3Jvc3MobnVtZXJpY19jb2xfY2hhcnRzLCBsaXN0KG1pbj1taW4sbWF4PW1heCxhdmc9bWVhbikpKQ0KY2FudF9zZW1hbmFzID0gY2hhcnRfZ3JvdXAgJT4lIHN1bW1hcmlzZV9hdCh3ZWVrX3N0YXJ0LCBuX2Rpc3RpbmN0KQ0KbmFtZXMoY2FudF9zZW1hbmFzJHdlZWtfc3RhcnQpIDwtICJjYW50X3NlbWFuYXMiDQoNCmFnZ3JlZ2F0aW9uX2RmIDwtIGNiaW5kKG51bWVyaWNfY2hhcnRzX3N1bW1hcml6ZXMsIGNhbnRfc2VtYW5hc1ssLWMoMTo3KV0sY29udGludWFzX3N1bW1hcml6ZWRbLC1jKDE6NyldLCBjYXRlZ29yaWNhc19zdW1tYXJpemVzWywtYygxOjcpXSkNCg0KbmFtZXMoYWdncmVnYXRpb25fZGYpW25hbWVzKGFnZ3JlZ2F0aW9uX2RmKSA9PSAnd2Vla19zdGFydCddIDwtICJjYW50X3NlbWFuYXMiDQoNCmNvbHMgPC0gbmFtZXMoYWdncmVnYXRpb25fZGYpDQpudW1lcmljX2NvbHMgPC0gY29sc1tzYXBwbHkoYWdncmVnYXRpb25fZGYsaXMubnVtZXJpYyldDQoNCnN1bW1hcnkoYWdncmVnYXRpb25fZGZbLG51bWVyaWNfY29sc1syOmxlbmd0aChudW1lcmljX2NvbHMpXV0pDQoNCg0KYGBgDQoNCg0KYGBge3J9DQpkZl9seXJpY3NfdW5pY2FzIDwtIGRmX2x5cmljcyAlPiUgZGlzdGluY3QoYXJ0aXN0X25hbWUsIHRyYWNrX25hbWUsIGx5cmljcykNCm5yb3coZGZfbHlyaWNzX3VuaWNhcykNCg0KZGZfY2hhcnRfd19seXJpY3MgPC0gbWVyZ2Uoam9pbl9hdWRpb19jaGFydHMsIGRmX2x5cmljc191bmljYXMsIGJ5LnggPSBjKCJhcnRpc3RfbmFtZSIsInRyYWNrX25hbWUiKSwgYnkueT0gYygiYXJ0aXN0X25hbWUiLCJ0cmFja19uYW1lIiksIGFsbC54PVRSVUUsIGFsbC55ID0gRkFMU0UpDQoNCmRmX2NoYXJ0X3dfbHlyaWNzIDwtIGRmX2NoYXJ0X3dfbHlyaWNzWyFpcy5uYShkZl9jaGFydF93X2x5cmljcyRseXJpY3MpLF0NCg0KYGBgDQoNCg0KDQojIEhJU1RPR1JBTUFTIFkgQkFSUExPVFMgREUgVkFSSUFCTEVTDQpgYGB7cn0NCg0KIyNoaXN0b2dyYW1hIGRlIGxhcyB2YXJpYWJsZXMgY29udGludWFzIGRlIGF1ZGlvX2ZlYXR1cmVzDQoNCmZvciAoaSBpbiBmZWF0dXJlc19jb250aW51YXMpew0KDQogIGhpc3QoZGZfYXVkaW9fZmVhdHVyZXNbLGldLCBtYWluID0gcGFzdGUoIkhpc3RvZ3JhbWEgZGUiLCBpLCAiKGFsbCBkYXRhKSIpLCB4bGFiID0gaSkNCiAgYWJsaW5lKHYgPSBtZWFuKGRmX2F1ZGlvX2ZlYXR1cmVzWyxpXSwgbmEucm0gPSBUUlVFKSAsIGNvbD0icmVkIikNCiAgYWJsaW5lKHYgPSBtZWRpYW4oZGZfYXVkaW9fZmVhdHVyZXNbLGldLCBuYS5ybSA9IFRSVUUpICwgY29sPSJibHVlIikNCiAgbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGMoIk1lZGlhIiwgIk1lZGlhbmEiKSwgY29sPWMoInJlZCIsICJibHVlIiksIGx0eSA9MSkNCg0KfQ0KDQojZGl2aWRvIGxvcyBmZWF0dXJlcyBwb3Igc3UgZGlzdHJpYnVjacOzbg0KZmVhdHVyZXNfY29udGludWFzX21lZGlhIDwtIGMoJ2RhbmNlYWJpbGl0eScsICd0ZW1wbycsICd2YWxlbmNlJykNCg0KZmVhdHVyZXNfY29udGludWFzX21lZGlhbmEgPC0gYygnYWNvdXN0aWNuZXNzJywgJ2R1cmF0aW9uX21zJywgJ2VuZXJneScsICdpbnN0cnVtZW50YWxuZXNzJywgJ2xpdmVuZXNzJywgJ2xvdWRuZXNzJywgJ3NwZWVjaGluZXNzJywgJ2NhbnRfbWFya2V0cycpDQoNCg0KIyNoaXN0b2dyYW1hIGRlIGxhcyB2YXJpYWJsZXMgY29udGludWFzIGRlIGNoYXJ0cw0KZm9yIChpIGluIGMoZmVhdHVyZXNfY29udGludWFzLCAiU3RyZWFtcyIpKXsNCg0KICBoaXN0KGpvaW5fYXVkaW9fY2hhcnRzWyxpXSwgbWFpbiA9IHBhc3RlKCJIaXN0b2dyYW1hIGRlIiwgaSwgICIoY2hhcnRzKSIpLCB4bGFiID0gaSkNCiAgYWJsaW5lKHYgPSBtZWFuKGpvaW5fYXVkaW9fY2hhcnRzWyxpXSwgbmEucm0gPSBUUlVFKSAsIGNvbD0icmVkIikNCiAgYWJsaW5lKHYgPSBtZWRpYW4oam9pbl9hdWRpb19jaGFydHNbLGldLCBuYS5ybSA9IFRSVUUpICwgY29sPSJibHVlIikNCg0KfQ0KDQojZGl2aWRvIGZlYXR1cmVzIGRlIGNoYXJ0cyBzZWfDum4gc3UgZGlzdHJpYnVjacOzbg0KYXVkaW9fY2hhcnRzX2NvbnRpbnVhc19tZWRpYSA8LSBjKCdkdXJhdGlvbl9tcycsICd2YWxlbmNlJykNCg0KYXVkaW9fY2hhcnRzX2NvbnRpbnVhc19tZWRpYW5hIDwtIGMoJ2RhbmNlYWJpbGl0eScsICdhY291c3RpY25lc3MnLCAndGVtcG8nLCAnZW5lcmd5JywgJ2luc3RydW1lbnRhbG5lc3MnLCAnbGl2ZW5lc3MnLCAnbG91ZG5lc3MnLCAnc3BlZWNoaW5lc3MnLCAnY2FudF9tYXJrZXRzJywgIlN0cmVhbXMiKQ0KDQoNCiMjbWVkaWRhcyByZXN1bWVuIHkgYmFycGxvdHMgZGUgbGFzIHZhcmlhYmxlcyBjYXRlZ29yaWNhcyBhdWRpb19mZWF0dXJlcw0KZm9yKGkgaW4gZmVhdHVyZXNfY2F0ZWdvcmljYXMpew0KDQogIGJhcnBsb3Qoc29ydCh0YWJsZShkZl9hdWRpb19mZWF0dXJlc1ssaV0pLGRlY3JlYXNpbmcgPSBUKSwgbGFzPTIsIA0KICAgICAgICAgIG1haW4gPSBwYXN0ZSgiQmFycGxvdCBkZSIsIGksICIoYWxsIGRhdGEpIikpDQogICMgcGllKHRhYmxlKGRmX2ZlYXR1cmVzX2NhdGVnb3JpY29zWyxpXSkpDQp9DQoNCg0KDQojI21lZGlkYXMgcmVzdW1lbiB5IGJhcnBsb3RzIGRlIGxhcyB2YXJpYWJsZXMgY2F0ZWdvcmljYXMgam9pbl9hdWRpb19jaGFydHMNCg0KZm9yKGkgaW4gZmVhdHVyZXNfY2F0ZWdvcmljYXMpew0KICANCiAgYmFycGxvdChzb3J0KHRhYmxlKGpvaW5fYXVkaW9fY2hhcnRzWyxpXSksZGVjcmVhc2luZyA9IFQpLCBsYXM9MiwgDQogICAgICAgICAgbWFpbiA9IHBhc3RlKCJCYXJwbG90IGRlIiwgaSwgIihjaGFydHMpIikpDQogICMgcGllKHRhYmxlKGRmX2ZlYXR1cmVzX2NhdGVnb3JpY29zWyxpXSkpDQp9DQoNCg0KYGBgDQoNCiMjIEFuYWxpc2lzIGRlIGxhIHZhcmlhYmxlIGBtYXJrZXRzX2NvbmNhdGANCg0KYGBge3J9DQojSGFnbyB1biBqb2luIGFsIHJldsOpcyANCg0KZGZfY2hhcnRfdG9qb2luIDwtIGRmX2NoYXJ0c1ssYygiVHJhY2tfTmFtZSIsICJBcnRpc3QiLCAiVVJMIildDQpkZl9jaGFydF90b2pvaW4kaXNpbmNoYXJ0IDwtIDENCmRmX2F1ZGlvX2ZlYXR1cmVzX3Rvam9pbiA8LSBkZl9hdWRpb19mZWF0dXJlc1ssIGMoInRyYWNrX25hbWUiLCJhcnRpc3Rfa2V5IiwiZXh0ZXJuYWxfdXJsc19zcG90aWZ5IiwibWFya2V0c19jb25jYXQiKV0NCg0Kam9pbl9iYXJwbG90IDwtIGRmX2F1ZGlvX2ZlYXR1cmVzX3Rvam9pbiAlPiUgDQogIHNlbGVjdCgidHJhY2tfbmFtZSIsImFydGlzdF9rZXkiLCJleHRlcm5hbF91cmxzX3Nwb3RpZnkiLCJtYXJrZXRzX2NvbmNhdCIpICU+JSANCiAgbGVmdF9qb2luKCBkZl9jaGFydF90b2pvaW4gJT4lDQogICAgICAgICAgICAgICBzZWxlY3QoIlRyYWNrX05hbWUiLCAiQXJ0aXN0IiwgIlVSTCIsImlzaW5jaGFydCIpLA0KICAgICAgICAgICAgICAgYnkgPSBjKA0KICAgICAgICAgICAgICAgICAidHJhY2tfbmFtZSIgPSAiVHJhY2tfTmFtZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICJhcnRpc3Rfa2V5IiA9IkFydGlzdCIsIA0KICAgICAgICAgICAgICAgICAgICAgICJleHRlcm5hbF91cmxzX3Nwb3RpZnkiID0gIlVSTCIpKQ0KDQoNCmpvaW5fYmFycGxvdCRpc2luY2hhcnRbaXMubmEoam9pbl9iYXJwbG90JGlzaW5jaGFydCldIDwtIDANCg0Kam9pbl9iYXJwbG90JGlzaW5jaGFydCA8LSBmYWN0b3Ioam9pbl9iYXJwbG90JGlzaW5jaGFydCkNCg0KdGFibGFfaXNpbmNoYXJ0IDwtIHRhYmxlKHVubGlzdChsYXBwbHkoam9pbl9iYXJwbG90W2pvaW5fYmFycGxvdCRpc2luY2hhcnQ9PTEsIm1hcmtldHNfY29uY2F0Il0sIGZ1bmN0aW9uKHgpIHN0cnNwbGl0KGFzLmNoYXJhY3Rlcih4KSwgJywnKSkpKQ0KDQp0YWJsYV9ub3RpbmNoYXJ0IDwtIHRhYmxlKHVubGlzdChsYXBwbHkoam9pbl9iYXJwbG90W2pvaW5fYmFycGxvdCRpc2luY2hhcnQ9PTAsIm1hcmtldHNfY29uY2F0Il0sIGZ1bmN0aW9uKHgpIHN0cnNwbGl0KGFzLmNoYXJhY3Rlcih4KSwgJywnKSkpKQ0KDQphbGxfY291bnRyaWVzIDwtIG5hbWVzKHRhYmxhX2lzaW5jaGFydCkNCg0KeGxhYnMgPC0gcGFzdGUocGFzdGUoaGVhZChhbGxfY291bnRyaWVzLDMpLCBjb2xsYXBzZSA9ICIsIiksIi4uLiIscGFzdGUodGFpbChhbGxfY291bnRyaWVzLDMpLGNvbGxhcHNlID0gIiwiKSwiKElTTy1Db2RlcyBkZSBQYWlzZXMpIiwgIGNvbGxhcHNlID0gIiwiKQ0KDQpvcHRpb25zKHNjaXBlbj05OTkpDQpwYXIobWZyb3cgPSBjKDEsMiksIGxhcz0xLCBtYXI9YygzLDMsNSwzKSwgb21hPWMoMCwxLDEsMSkpDQpiYXJwbG90KHNvcnQodGFibGFfaXNpbmNoYXJ0LCBkZWNyZWFzaW5nID0gVFJVRSksIG5hbWVzLmFyZz0iIiwgbWFpbiA9J0VuIENoYXJ0cycsY29sPXJnYigwLjIsMC40LDAuNiwwLjYpLHhsYWIgPSAiUGFpc2VzIChJU08tQ29kZXMpIikNCiMgbXRleHQoc2lkZSA9IDEsIHRleHQgPSB4bGFicywgbGluZSA9IDEpDQpiYXJwbG90KHNvcnQodGFibGFfbm90aW5jaGFydCwgZGVjcmVhc2luZyA9IFRSVUUpLCBuYW1lcy5hcmcgPSAiIiwgbWFpbj0nRnVlcmEgZGUgQ2hhcnRzJyxjb2w9cmdiKDAuMiwwLjQsMC42LDAuNiksIHhsYWIgPSAiUGFpc2VzIChJU08tQ29kZXMpIikNCm10ZXh0KHNpZGUgPSAxLCB0ZXh0ID0geGxhYnMsIGxpbmUgPSAxLCBhZGogPSAyKQ0KbXRleHQoIkZyZWN1ZW5jaWEgZGUgbWVyY2Fkb3MgaGFiaWxpdGFkb3MiLCBzaWRlID0gMywgbGluZSA9IC0xLCBvdXRlciA9IFRSVUUsIGNleCA9IDEuMywgZm9udCA9MiApDQojIG10ZXh0KCJQYWlzZXMgKElTTy1Db2RlcykiLCBzaWRlID0gMywgbGluZSA9IC0yNSwgb3V0ZXIgPSBUUlVFKQ0KYGBgDQoNCg0KIyBDT1JSRUxBQ0lPTkVTDQpgYGB7cn0NCiNjb3JyZWxhY2lvbmVzIGVuIGF1ZGlvIGZlYXR1cmVzDQp4IDwtIGNvcihkZl9hdWRpb19mZWF0dXJlc1ssYyhmZWF0dXJlc19jb250aW51YXNfbWVkaWEsIGZlYXR1cmVzX2NvbnRpbnVhc19tZWRpYW5hKV0sICB1c2UgPSAgImNvbXBsZXRlLm9icyIpDQpjb3JycGxvdCh4LCB0eXBlID0gInVwcGVyIiwgdGl0bGUgPSAiQ29ycmVsYWNpb24gZGUgYXRyaWJ1dG9zIGRlIGF1ZGlvX2ZlYXR1cmVzIiwgbWFyPWMoMCwwLDEsMCksIG1ldGhvZD0ibnVtYmVyIiAsbnVtYmVyLmNleD0wLjcpDQpgYGANCg0KDQpgYGB7cn0NCg0KI2NvcnJlbGFjaW9uZXMgZW4gY2hhcnRzDQp4IDwtIGNvcihzY2FsZShqb2luX2F1ZGlvX2NoYXJ0c1ssYyhhdWRpb19jaGFydHNfY29udGludWFzX21lZGlhLCBhdWRpb19jaGFydHNfY29udGludWFzX21lZGlhbmEpXSksIHVzZSA9ICAiY29tcGxldGUub2JzIikNCmNvcnJwbG90KHgsIHR5cGUgPSAidXBwZXIiLCB0aXRsZSA9ICJDb3JyZWxhY2lvbiBkZSBhdHJpYnV0b3MgZGUgbG9zIENoYXJ0cyIsIG1hcj1jKDAsMCwxLDApLCBtZXRob2Q9Im51bWJlciIsIG51bWJlci5jZXg9MC43ICkNCmBgYA0KDQpgYGB7cn0NCg0KI2NoaTIgdGVzdCAjY29uIG4gZ3JhbmRlIG5vIHNlIHB1ZWRlIHVzYXIgZXN0ZSB0ZXN0DQp0YWJsYV9rZXlfYWxidW0gPC0gdGFibGUoZGZfYXVkaW9fZmVhdHVyZXMka2V5X25hbWUsIGRmX2F1ZGlvX2ZlYXR1cmVzJGFsYnVtX3R5cGUpDQpjYXQoIlRhYmxhIGRlIGNvbnRpZ2VuY2lhIGVudHJlIGtleSB5IGFsYnVtIHR5cGVcbiIpDQp0YWJsYV9rZXlfYWxidW0NCmNoaXNxLnRlc3QodGFibGFfa2V5X2FsYnVtKQ0KYGBgDQoNCg0KIyBTRVNHTyBERSBWQVJJQUJMRVMgDQoNCiMjIEJveHBsb3RzIFZhcmlhYmxlcyBOdW3DqXJpY2FzIHNpbiBmaWx0cmFyIG91dGxpZXJzDQpgYGB7cn0NCiNkaXZpZG8gbG9zIGZlYXR1cmVzIHBvciBzdSBkaXN0cmlidWNpw7NuDQpmZWF0dXJlc19jb250aW51YXNfbWVkaWEgPC0gYygnZGFuY2VhYmlsaXR5JywgJ3RlbXBvJywgJ3ZhbGVuY2UnKQ0KDQpmZWF0dXJlc19jb250aW51YXNfbWVkaWFuYSA8LSBjKCdhY291c3RpY25lc3MnLCAnZHVyYXRpb25fbXMnLCAnZW5lcmd5JywgJ2luc3RydW1lbnRhbG5lc3MnLCAnbGl2ZW5lc3MnLCAnbG91ZG5lc3MnLCAnc3BlZWNoaW5lc3MnLCAnY2FudF9tYXJrZXRzJykNCg0KYWxsX2ZlYXR1cmVzIDwtIGMoZmVhdHVyZXNfY29udGludWFzX21lZGlhLCBmZWF0dXJlc19jb250aW51YXNfbWVkaWFuYSkNCg0KcGFyKG1mcm93PWMoNCwzKSkNCmZvciAoZmVhdHVyZSBpbiBhbGxfZmVhdHVyZXMpew0KICBib3hwbG90KGRmX2F1ZGlvX2ZlYXR1cmVzWyxmZWF0dXJlXSwgbGFzPTIsIGhvcml6b250YWw9VCwgbWFpbj1mZWF0dXJlKQ0KfQ0KYGBgDQoNCkNvbiBleGNlcGNpw7NuIGRlIHZhbGVuY2UgZWwgcmVzdG8gZGUgbGFzIGZlYXR1cmVzIHBvc2XDrWFuIGNpZXJ0byBzZXNnby4gU2UgZGVjaWRpw7MgdHJhbnNmb3JtYXIgbGFzIHZhcmlhYmxlcyBxdWUgbWF5b3Igc2VzZ28gcG9zZcOtYW46IGR1cmF0aW9uX21zLCBpbnN0cnVtZW50YWxuZXNzLCBsaXZlbmVzcywgc3BlZWNoaW5lc3MgY29tbyBtw6l0b2RvIGRlIGNvcnJlZ2lyIGxhIGRpc3RyaWJ1Y2nDs24geSBhY2hpY2FyIGxhIGNhbnRpZGFkIGRlIG91dGxpZXJzLiBMYSB2YXJpYWJsZSBsb3VkbmVzc19yZWdfaW1wIG5vIGZ1ZSBtb2RpZmljYWRhIGRlYmlkbyBhIHF1ZSBhbCBzZXIgbmVnYXRpdmEgDQoNCg0KYGBge3J9DQojICJkYW5jZWFiaWxpdHksdGVtcG8sdmFsZW5jZSxhY291c3RpY25lc3MsZHVyYXRpb25fbXMsZW5lcmd5LGluc3RydW1lbnRhbG5lc3MsbGl2ZW5lc3Msc3BlZWNoaW5lc3MsY2FudF9tYXJrZXRzIg0KDQojc2VzZ29zIGQgbGFzIHZhcmlhYmxlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0Kc29ydChhcHBseShkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZXNfY29udGludWFzXSwgTUFSR0lOID0gMiwgZnVuY3Rpb24oeCl7ICgzKiAobWVhbih4LG5hLnJtID0gVCktbWVkaWFuKHgsIG5hLnJtID0gVCkpKS9zZCh4LCBuYS5ybSA9IFQpfSApKQ0KDQp2YXJpYWJsZXNfc2VzZ28gPC0gdW5saXN0KHN0cnNwbGl0KCJhY291c3RpY25lc3MsZHVyYXRpb25fbXMsaW5zdHJ1bWVudGFsbmVzcyxsaXZlbmVzcyxzcGVlY2hpbmVzcyxjYW50X21hcmtldHMsZW5lcmd5IiwgIiwiKSkNCg0KZGZfc2VzZ2FkYXMgPC0gZGZfYXVkaW9fZmVhdHVyZXNbLHZhcmlhYmxlc19zZXNnb10NCg0KbG9nYXJpdG1vX2FqdXN0YWRvID0gZnVuY3Rpb24oeCxkZWx0YSl7DQogIGlmICh4PT0wLjApew0KICAgIHJldHVybihsb2coMC4wMCtkZWx0YSwgYmFzZSA9IDEwKSkNCiAgfWVsc2V7DQogICAgcmV0dXJuKGxvZyh4LCBiYXNlID0gMTApKQ0KICB9DQp9DQoNCmRlbHRhIDwtIDEwXigtNikNCg0KZGZfc2VzZ2FkYXNfbG9nX2FkanVzdCA8LSBkYXRhLmZyYW1lKGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzWyx2YXJpYWJsZXNfc2VzZ29dLCBNQVJHSU4gPSBjKDEsMiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIGxvZ2FyaXRtb19hanVzdGFkbyh4LGRlbHRhKSkpDQojIG5hbWVzKGRmX3Nlc2dhZGFzX2xvZ19hZGp1c3QpIDwtIHBhc3RlKG5hbWVzKGRmX3Nlc2dhZGFzKSwgIl9sb2ciLCBzZXA9IiIpDQpuYW1lcyhkZl9zZXNnYWRhc19sb2dfYWRqdXN0KSA8LSBuYW1lcyhkZl9zZXNnYWRhcykNCg0KZGZfZGF0b3MgPC0gY2JpbmQoZGZfc2VzZ2FkYXMsIGRmX3Nlc2dhZGFzX2xvZ19hZGp1c3QpDQoNCg0KDQphIDwtIGRmX3Nlc2dhZGFzDQpiIDwtIGRmX3Nlc2dhZGFzX2xvZ19hZGp1c3QNCm5hbWVzKGIpIDwtIHBhc3RlKG5hbWVzKGRmX3Nlc2dhZGFzKSwgIl9sb2ciLCBzZXA9IiIpDQptZXJnZWQgPC0gY2JpbmQoYSxiKQ0KDQptZXJnZWQgPC0gbWVyZ2VkWywgb3JkZXIobmFtZXMobWVyZ2VkKSldDQoNCnJvdW5kKHNvcnQoYXBwbHkobWVyZ2VkLCBNQVJHSU4gPSAyLCBmdW5jdGlvbih4KXsgKDMqIChtZWFuKHgsbmEucm0gPSBUKS1tZWRpYW4oeCwgbmEucm0gPSBUKSkpL3NkKHgsIG5hLnJtID0gVCl9KSksMikNCg0KYGBgDQoNCg0KYGBge3J9DQoNCnZhcmlhYmxlc19wbG90IDwtIHVubGlzdChzdHJzcGxpdCgiZHVyYXRpb25fbXMiLCAiLCIpKQ0KdmFyaWFibGVzX3Bsb3QgPC0gYXBwZW5kKHZhcmlhYmxlc19wbG90LHBhc3RlKHZhcmlhYmxlc19wbG90LCJfbG9nIiwgc2VwPSIiKSkNCnZhcmlhYmxlc19wbG90IDwtIHZhcmlhYmxlc19wbG90W29yZGVyKHZhcmlhYmxlc19wbG90KV0NCnBsb3RlYXIgPC0gbWVyZ2VkWyx2YXJpYWJsZXNfcGxvdF0NCg0KcGFyKG1mcm93ID0gYygxLDIpKQ0KZm9yIChjb2wgaW4gbmFtZXMocGxvdGVhcikpew0KICBoaXN0KHBsb3RlYXJbLGNvbF0sIGJyZWFrcz0iRkQiLCBtYWluPWNvbCwgeGxhYj0iIikNCn0NCg0KYGBgDQoNCg0KDQpgYGB7cn0NCnN1bW1hcnkoZGZfYXVkaW9fZmVhdHVyZXNbLGFsbF9mZWF0dXJlc10pDQpgYGANCmBgYHtyfQ0KDQoNCmhpc3QobG9nKGRmX2F1ZGlvX2ZlYXR1cmVzJGR1cmF0aW9uX21zKSkNCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KDQp0cmFuc2Zvcm1hY2lvbiA8LSBjKCdpbnN0cnVtZW50YWxuZXNzJywnbG91ZG5lc3MnLCdsaXZlbmVzcycsJ3NwZWVjaGluZXNzJywgJ2R1cmF0aW9uX21zJykNCg0KbG9nYXJpdG1vX2FqdXN0YWRvID0gZnVuY3Rpb24oeCxkZWx0YSl7DQogIGlmICh4PD0wLjApew0KICAgIHJldHVybihsb2coMC4wMCtkZWx0YSwgYmFzZSA9IDEwKSkNCiAgfWVsc2V7DQogICAgcmV0dXJuKGxvZyh4LCBiYXNlID0gMTApKQ0KICB9DQp9DQoNCmRlbHRhIDwtIDEwXigtNikNCg0KcGFyKG1mcm93PWMoMiw0KSkNCmZvciAoZmVhdHVyZSBpbiB0cmFuc2Zvcm1hY2lvbil7DQogIGhpc3QoZGZfYXVkaW9fZmVhdHVyZXNbLGZlYXR1cmVdLCBtYWluPWZlYXR1cmUpDQp9DQpmb3IgKGZlYXR1cmUgaW4gdHJhbnNmb3JtYWNpb24pew0KICBoaXN0KHVubGlzdChsYXBwbHkoZGZfYXVkaW9fZmVhdHVyZXNbLGZlYXR1cmVdLCBmdW5jdGlvbih4KSBsb2dhcml0bW9fYWp1c3RhZG8oeCxkZWx0YSkpKSwgbWFpbj1wYXN0ZShmZWF0dXJlLCJsb2ciLCBzZXA9Il8iKSkNCn0NCmBgYA0KDQpgYGB7cn0NCg0KaW52X3NxcnRfYWp1c3RhZGEgPSBmdW5jdGlvbih4LCBkZWx0YSl7DQogIGlmICh4PT0wLjApew0KICAgIHJldHVybigxL3NxcnQoeCtkZWx0YSkpDQogIH1lbHNlew0KICAgIHJldHVybigxL3NxcnQoeCkpDQogIH0NCn0NCg0KDQpkZWx0YSA8LSAxMF4oLTYpDQoNCnBhcihtZnJvdz1jKDIsNCkpDQpmb3IgKGZlYXR1cmUgaW4gdHJhbnNmb3JtYWNpb24pew0KICBoaXN0KGRmX2F1ZGlvX2ZlYXR1cmVzWyxmZWF0dXJlXSwgbWFpbj1mZWF0dXJlKQ0KfQ0KZm9yIChmZWF0dXJlIGluIHRyYW5zZm9ybWFjaW9uKXsNCiAgaGlzdCh1bmxpc3QobGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzWyxmZWF0dXJlXSwgZnVuY3Rpb24oeCkgaW52X3NxcnRfYWp1c3RhZGEoeCxkZWx0YSkpKSwgbWFpbj1wYXN0ZShmZWF0dXJlLCJpbnZfc3F0Iiwgc2VwPSJfIikpDQp9DQoNCg0KDQpgYGANCg0KDQpgYGB7cn0NCg0KDQpwYXIobWZyb3c9YygyLDQpKQ0KZm9yIChmZWF0dXJlIGluIHRyYW5zZm9ybWFjaW9uKXsNCiAgaGlzdChkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZV0sIG1haW49ZmVhdHVyZSkNCn0NCmZvciAoZmVhdHVyZSBpbiB0cmFuc2Zvcm1hY2lvbil7DQogIGhpc3Qoc3FydChkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZV0pLCBtYWluPXBhc3RlKGZlYXR1cmUsInNxcnQiLCBzZXA9Il8iKSkNCn0NCg0KYGBgDQoNCg0KDQpgYGB7cn0NCg0KcGFyKG1mcm93ID0gYygyLDEpKSANCmhpc3QoZGZfYXVkaW9fZmVhdHVyZXNbLCdsb3VkbmVzc19yZWdfaW1wJ10sIG1haW49J2xvdWRuZXNzJywgeGxhYj0iIikNCiNoaXN0KHNxcnQoZGZfYXVkaW9fZmVhdHVyZXNbLCdsb3VkbmVzc19yZWdfaW1wJ10pLCBtYWluPSAnbG91ZG5lc3Nfc3FydCcsIHhsYWI9IiIpDQpib3hwbG90KGRmX2F1ZGlvX2ZlYXR1cmVzWywnbG91ZG5lc3NfcmVnX2ltcCddLCBob3Jpem9udGFsID0gVCkNCiNib3hwbG90KHNxcnQoZGZfYXVkaW9fZmVhdHVyZXNbLCdsb3VkbmVzc19yZWdfaW1wJ10pLCBob3Jpem9udGFsID0gVCkNCg0KDQoNCg0KDQpgYGANCg0KYGBge3J9DQpmaXQgPC0gbG0obG91ZG5lc3N+ZW5lcmd5K2Fjb3VzdGljbmVzcywgZGF0YT1kZl9hdWRpb19mZWF0dXJlcykNCg0KbW9kZWxvIDwtIGZpdCRjb2VmZmljaWVudHMNCg0KZGZfYXVkaW9fZmVhdHVyZXMkbG91ZG5lc3NfcmVnX2ltcCA8LSBkZl9hdWRpb19mZWF0dXJlcyRsb3VkbmVzcw0KDQpYIDwtIGRmX2F1ZGlvX2ZlYXR1cmVzW2RmX2F1ZGlvX2ZlYXR1cmVzJGxvdWRuZXNzPjAsIGMoJ2VuZXJneScsICJhY291c3RpY25lc3MiKV0NCg0KZGZfYXVkaW9fZmVhdHVyZXMkbG91ZG5lc3NfcmVnX2ltcFtkZl9hdWRpb19mZWF0dXJlcyRsb3VkbmVzcz4wXSA8LSBtb2RlbG9bMV0rbW9kZWxvWzJdKlhbLDFdK21vZGVsb1szXSpYWywyXQ0KDQpzdW1tYXJ5KGRmX2F1ZGlvX2ZlYXR1cmVzWyxjKCJsb3VkbmVzcyIsICJsb3VkbmVzc19yZWdfaW1wIildKQ0KDQpzdW1tYXJ5KGZpdCkNCmBgYA0KDQoNCg0KYGluc3RydW1lbnRhbG5lc3NgIHRpZW5lIG11Y2hvIHNlc2dvIGxhIHZhcmlhYmxlLiBTZSB2YSBhIHJlY3VycmlyIGEgdW5hIGxvZ2FyaXRtaXphY2nDs24gZGUgbGEgdmFyaWFibGUsIHByZXZpYSB0cmFuc2Zvcm1hY2nDs24gZGVsIGRvbWluaW8sIGhhY2llbmRvIHF1ZSBsb3MgdmFsb3JlcyBxdWUgc29uIDAsIHNlYW4gZW4gcmVhbGlkYWQgMC4wMDAwMDAxICANCg0KYGBge3J9DQpsb2dhcml0bW9fYWp1c3RhZG8gPSBmdW5jdGlvbih4LGRlbHRhKXsNCiAgaWYgKHg9PTAuMCl7DQogICAgcmV0dXJuKGxvZyh4K2RlbHRhLCBiYXNlID0gMTApKQ0KICB9ZWxzZXsNCiAgICByZXR1cm4obG9nKHgsIGJhc2UgPSAxMCkpDQogIH0NCn0NCg0KZGVsdGEgPC0gMTBeKC02KQ0KDQpkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzX2xvZ2FkanVzdCA8LSB1bmxpc3QobGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzJGluc3RydW1lbnRhbG5lc3MsIGZ1bmN0aW9uKHgpIGxvZ2FyaXRtb19hanVzdGFkbyh4LGRlbHRhKSkpDQoNCnBhcihtZnJvdyA9YygyLDIpKQ0KaGlzdChkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzLCBtYWluPSJpbnNydW1lbnRhbG5lc3MiLCB4bGFiPSIiKQ0KaGlzdCh1bmxpc3QobGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzJGluc3RydW1lbnRhbG5lc3MsIGZ1bmN0aW9uKHgpIGxvZ2FyaXRtb19hanVzdGFkbyh4LGRlbHRhKSkpLCBtYWluPSdpbnN0cnVtZW50YWxuZXNzX2xvZ2FkanVzdCcsIHlsaW0gPSBjKDAsMTMwNTAwKSwgeGxhYiA9ICIiKQ0KYm94cGxvdChkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzLCBtYWluPSIiLCBob3Jpem9udGFsID0gVCkNCmJveHBsb3QodW5saXN0KGxhcHBseShkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzLCBmdW5jdGlvbih4KSBsb2dhcml0bW9fYWp1c3RhZG8oeCxkZWx0YSkpKSwgbWFpbj0iIiwgaG9yaXpvbnRhbD1UKQ0KIyBoaXN0KGxvZygxL3NxcnQoZGZfYXVkaW9fZmVhdHVyZXMkaW5zdHJ1bWVudGFsbmVzcyswLjAwMDAxKSksbWFpbj0nbG9nKHNxcnQoeCspKScsIHlsaW09YygwLDEzMDUwMCksIHhsYWIgPSAiIikNCg0KYGBgDQoNCsK/RXMgw7p0aWwgZXN0YSB0cmFuc2Zvcm1hY2nDs24/IA0KDQpgYGB7cn0NCg0KZGVsdGEgPC0gMTBeKC02KQ0KDQpkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzX2xvZ2FkanVzdCA8LSB1bmxpc3QobGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzJGluc3RydW1lbnRhbG5lc3MsIGZ1bmN0aW9uKHgpIGxvZ2FyaXRtb19hanVzdGFkbyh4LGRlbHRhKSkpDQoNCmRmX2NoYXJ0X3Rvam9pbiA8LSBkZl9jaGFydHNbLGMoIlRyYWNrX05hbWUiLCAiQXJ0aXN0IiwgIlVSTCIpXQ0KZGZfY2hhcnRfdG9qb2luJGlzaW5jaGFydCA8LSAxDQpkZl9hdWRpb19mZWF0dXJlc190b2pvaW4gPC0gZGZfYXVkaW9fZmVhdHVyZXNbLCBjKCJ0cmFja19uYW1lIiwiYXJ0aXN0X2tleSIsImV4dGVybmFsX3VybHNfc3BvdGlmeSIsImluc3RydW1lbnRhbG5lc3MiLCAiaW5zdHJ1bWVudGFsbmVzc19sb2dhZGp1c3QiKV0NCg0Kam9pbl9oaXN0b2dyYW0gPC0gZGZfYXVkaW9fZmVhdHVyZXNfdG9qb2luICU+JSANCiAgZHBseXI6OnNlbGVjdCgidHJhY2tfbmFtZSIsImFydGlzdF9rZXkiLCJleHRlcm5hbF91cmxzX3Nwb3RpZnkiLCJpbnN0cnVtZW50YWxuZXNzIiwgImluc3RydW1lbnRhbG5lc3NfbG9nYWRqdXN0IikgJT4lIA0KICBsZWZ0X2pvaW4oIGRmX2NoYXJ0X3Rvam9pbiAlPiUNCiAgICAgICAgICAgICAgIHNlbGVjdCgiVHJhY2tfTmFtZSIsICJBcnRpc3QiLCAiVVJMIiwiaXNpbmNoYXJ0IiksDQogICAgICAgICAgICAgICBieSA9IGMoDQogICAgICAgICAgICAgICAgICJ0cmFja19uYW1lIiA9ICJUcmFja19OYW1lIiwgDQogICAgICAgICAgICAgICAgICAgICAgImFydGlzdF9rZXkiID0iQXJ0aXN0IiwgDQogICAgICAgICAgICAgICAgICAgICAgImV4dGVybmFsX3VybHNfc3BvdGlmeSIgPSAiVVJMIikpDQoNCg0Kam9pbl9oaXN0b2dyYW0kaXNpbmNoYXJ0W2lzLm5hKGpvaW5faGlzdG9ncmFtJGlzaW5jaGFydCldIDwtIDANCg0Kam9pbl9oaXN0b2dyYW0kaXNpbmNoYXJ0IDwtIGZhY3Rvcihqb2luX2hpc3RvZ3JhbSRpc2luY2hhcnQpDQoNCg0KaDExIDwtIGhpc3Qoam9pbl9oaXN0b2dyYW1bam9pbl9oaXN0b2dyYW0kaXNpbmNoYXJ0PT0xLCdpbnN0cnVtZW50YWxuZXNzJ10pDQpoMTEkZGVuc2l0eSA8LSAgaDExJGNvdW50cy9zdW0oaDExJGNvdW50cykqMTAwDQoNCmgxMiA8LSBoaXN0KGpvaW5faGlzdG9ncmFtW2pvaW5faGlzdG9ncmFtJGlzaW5jaGFydD09MCwnaW5zdHJ1bWVudGFsbmVzcyddKQ0KaDEyJGRlbnNpdHkgPC0gIGgxMiRjb3VudHMvc3VtKGgxMiRjb3VudHMpKjEwMA0KDQpoMjEgPC0gaGlzdChqb2luX2hpc3RvZ3JhbVtqb2luX2hpc3RvZ3JhbSRpc2luY2hhcnQ9PTEsJ2luc3RydW1lbnRhbG5lc3NfbG9nYWRqdXN0J10pDQpoMjEkZGVuc2l0eSA8LSAgaDIxJGNvdW50cy9zdW0oaDIxJGNvdW50cykqMTAwDQoNCmgyMiA8LSBoaXN0KGpvaW5faGlzdG9ncmFtW2pvaW5faGlzdG9ncmFtJGlzaW5jaGFydD09MCwnaW5zdHJ1bWVudGFsbmVzc19sb2dhZGp1c3QnXSkNCmgyMiRkZW5zaXR5IDwtICBoMjIkY291bnRzL3N1bShoMjIkY291bnRzKSoxMDANCg0KI3BuZygiQzovVXNlcnMvQXN1cy9EZXNrdG9wL0RBVEEgU0NJRU5DRS9NQUVTVFJJQS9EYXRhIE1pbmluZy9UUC9ncmFmaWNvcy9pbnN0cnVtZW50YWxuZXNzLnBuZyIsDQojICAgIHdpZHRoID0gODAwLCBoZWlnaHQgPSA4MDApDQpwYXIobWZyb3cgPSBjKDMsMikpDQpwbG90KGgxMSwgbWFpbj0naW5zdHJ1bWVudGFsbmVzcyBcbmNoYXJ0JywgeGxhYj0iIiwgeWxhYj0iUG9yY2VudGFnZSIsIGZyZXE9RkFMU0UsIGNvbD0nZ3JleScsIHlsaW0gPSBjKDAsMTAwKSkNCnBsb3QoaDEyLCBtYWluPSdpbnN0cnVtZW50YWxuZXNzIFxuZnVlcmEgY2hhcnQnLCB4bGFiPSIiLCB5bGFiPSJQb3JjZW50YWdlIiwgZnJlcT1GQUxTRSwgY29sPSdncmV5JywgeWxpbSA9IGMoMCwxMDApKQ0KcGxvdChoMjEsIG1haW4gPSJpbnN0cnVtZW50YWxuZXNzX2xvZyBcbmNoYXJ0IiwgeGxhYj0iIiwgeWxhYj0iUG9yY2VudGFnZSIsIGZyZXE9RkFMU0UsIGNvbD0nZ3JleScsIHlsaW0gPSBjKDAsMTAwKSkNCnBsb3QoaDIyLCBtYWluID0iaW5zdHJ1bWVudGFsbmVzc19sb2cgXG5mdWVyYSBjaGFydCIsIHhsYWI9IiIsIHlsYWI9IlBvcmNlbnRhZ2UiLCBmcmVxPUZBTFNFLCBjb2w9J2dyZXknLCB5bGltID0gYygwLDEwMCkpDQpib3hwbG90KGpvaW5faGlzdG9ncmFtW2pvaW5faGlzdG9ncmFtJGlzaW5jaGFydD09MSwnaW5zdHJ1bWVudGFsbmVzc19sb2dhZGp1c3QnXSwgbWFpbj0iaW5zdHJ1bWVudGFsbmVzc19sb2cgY2hhcnQiLCBob3Jpem9udGFsID0gVCkNCmJveHBsb3Qoam9pbl9oaXN0b2dyYW1bam9pbl9oaXN0b2dyYW0kaXNpbmNoYXJ0PT0wLCdpbnN0cnVtZW50YWxuZXNzX2xvZ2FkanVzdCddLCBtYWluPSJpbnN0cnVtZW50YWxuZXNzX2xvZyBmdWVyYSBjaGFydCIsIGhvcml6b250YWwgPSBUKQ0KI2Rldi5vZmYoKQ0KDQpgYGANCg0KDQoNCiMjIyBaLVNjb3JlIGRlIFZhcmlhYmxlcyBxdWUgInRpZW5kZW4gYSBsYSBub3JtYWwiDQpgYGB7cn0NCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KIyMgRklMVFJBTU9TIE9VVExJRVJTIFBPUiBaLVNDT1JFIHBhcmEgJ2RhbmNlYWJpbGl0eScsICd0ZW1wbycsICd2YWxlbmNlJw0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KI3otc2NvcmUgcGFyYSB2YXJpYWJsZXMgcXVlIHRpZW5kZW4gYSBsYSBub3JtYWwNCiNmaWx0cm8gZmVhdHVyZXMgbnVtZXJpY29zIA0KDQojZGl2aWRvIGxvcyBmZWF0dXJlcyBwb3Igc3UgZGlzdHJpYnVjacOzbg0KZmVhdHVyZXNfY29udGludWFzX21lZGlhIDwtIGMoJ2RhbmNlYWJpbGl0eScsICd0ZW1wbycsICd2YWxlbmNlJykNCmRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSA8LSBkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZXNfY29udGludWFzX21lZGlhXQ0KDQojbm9ybWFsaXpvIHogc2NvcmUgY29uIGxhcyB2YXJpYWJsZXMgcXVlIHRpZW5kZW4gYSBsYSBub3JtYWwNCg0KenNjb3JlX2NvbHMgPC0gYygpDQpmb3IoY29sIGluIG5hbWVzKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSkpew0KICBuYW1lX2NvbCA8LSBwYXN0ZSgnenNjb3JlXycsY29sLCBzZXAgPSAiIikNCiAgenNjb3JlX2NvbHMgPC0gYXBwZW5kKHpzY29yZV9jb2xzLCBuYW1lX2NvbCkNCiAgbWVkaWEgPC0gIG1lYW4oZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhWyxjb2xdKQ0KICBzdGR2IDwtIHNkKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYVssY29sXSkNCiAgZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhWyxuYW1lX2NvbF0gPC0gKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYVssY29sXSAtIG1lZGlhKS9zdGR2DQogIH0NCg0KcGFyKG1mcm93PWMoMSxsZW5ndGgoenNjb3JlX2NvbHMpKSkNCmxhcHBseSh6c2NvcmVfY29scywgZnVuY3Rpb24oY29sKSBib3hwbG90KGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYVssY29sXSx4bGFiPWNvbCkpDQpgYGANCg0KIyMjIEFuYWxpc2lzIGRlIFotU2NvcmUgcG9yIHZhcmlhYmxlDQogDQojIyMjIERhbmNlYWJpbGl0eQ0KDQpgYGB7cn0NCiN2YXJpYWJsZTogZGFuY2VhYmlsaXR5DQoNCnVtYnJhbF96c2NvcmUgPC0gMw0KY29uZGl0aW9ucyA8LSAoZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhJHpzY29yZV9kYW5jZWFiaWxpdHk+IHVtYnJhbF96c2NvcmUpIHwgKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSR6c2NvcmVfZGFuY2VhYmlsaXR5PCAtMSp1bWJyYWxfenNjb3JlKQ0KZGZfYXVkaW9fZmVhdHVyZXNbY29uZGl0aW9ucyxdICU+JQ0KICBzZWxlY3QoYWxidW1fbmFtZSxhcnRpc3RfbmFtZSwgZGFuY2VhYmlsaXR5ICkgJT4lDQogIGFycmFuZ2UoLWRhbmNlYWJpbGl0eSkNCmBgYA0KDQojIyMjIFRlbXBvDQoNCmBgYHtyfQ0KI3ZhcmlhYmxlOiBUZW1wbw0KDQp1bWJyYWxfenNjb3JlIDwtIDMNCmNvbmRpdGlvbnMgPC0gKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSR6c2NvcmVfdGVtcG8+IHVtYnJhbF96c2NvcmUpIHwgKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSR6c2NvcmVfdGVtcG88IC0xKnVtYnJhbF96c2NvcmUpDQpkZl9hdWRpb19mZWF0dXJlc1tjb25kaXRpb25zLF0gJT4lDQogIHNlbGVjdChhbGJ1bV9uYW1lLGFydGlzdF9uYW1lLCB0ZW1wbyApICU+JQ0KICBhcnJhbmdlKC10ZW1wbykNCmBgYA0KDQojIyMjIFZhbGVuY2UNCg0KYGBge3J9DQojdmFyaWFibGU6IHZhbGVuY2UNCnVtYnJhbF96c2NvcmUgPC0gMw0KY29uZGl0aW9ucyA8LSAoZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhJHpzY29yZV92YWxlbmNlPiB1bWJyYWxfenNjb3JlKSB8IChkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWEkenNjb3JlX3ZhbGVuY2U8IC0xKnVtYnJhbF96c2NvcmUpDQpkZl9hdWRpb19mZWF0dXJlc1tjb25kaXRpb25zLF0gJT4lDQogIHNlbGVjdChhbGJ1bV9uYW1lLGFydGlzdF9uYW1lLCB2YWxlbmNlICkgJT4lDQogIGFycmFuZ2UoLXZhbGVuY2UpDQpgYGANCg0KIyMjIFotU2NvcmUgTW9kaWZpY2FkbyBkZSBWYXJpYWJsZXMgQXNpbWV0cmljYXMNCg0KYGBge3J9DQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyBGSUxUUkFNT1MgT1VUTElFUlMgUE9SIFotU0NPUkUgTU9ESUZJQ0FETyBwYXJhICdhY291c3RpY25lc3MnLCAnZHVyYXRpb25fbXMnLCAnZW5lcmd5JywgICdpbnN0cnVtZW50YWxuZXNzJywgJ2xpdmVuZXNzJywgJ2xvdWRuZXNzJywgJ3NwZWVjaGluZXNzJywgJ2NhbnRfbWFya2V0cycNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCmZlYXR1cmVzX2NvbnRpbnVhc19tZWRpYW5hIDwtIGMoJ2Fjb3VzdGljbmVzcycsICdkdXJhdGlvbl9tcycsICdlbmVyZ3knLCAnaW5zdHJ1bWVudGFsbmVzcycsICdsaXZlbmVzcycsICdsb3VkbmVzcycsICdzcGVlY2hpbmVzcycsICdjYW50X21hcmtldHMnKQ0KDQpkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWFuYSA8LSBkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZXNfY29udGludWFzX21lZGlhbmFdDQoNCg0KDQp6c2NvcmVtb2RpZl9jb2xzIDwtIGMoKQ0KZm9yKGNvbCBpbiBuYW1lcyhkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWFuYSkpew0KICBuYW1lX2NvbCA8LSBwYXN0ZSgnenNjb3JlbW9kaWZfJyxjb2wsIHNlcCA9ICIiKQ0KICB6c2NvcmVtb2RpZl9jb2xzIDwtIGFwcGVuZCh6c2NvcmVtb2RpZl9jb2xzLCBuYW1lX2NvbCkNCiAgbWVkID0gbWVkaWFuKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYW5hWyxjb2xdLCBuYS5ybSA9IFQpDQogIE1BRCA9IG1lZGlhbihhYnMoZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhbmFbLGNvbF0gLSBtZWQpLCBuYS5ybSA9IFQpDQogIGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYW5hWywgbmFtZV9jb2xdIDwtIDAuNjc0NSAqIChkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWFuYVssY29sXSAtIG1lZCkgLyBNQUQNCn0NCg0KDQpwYXIobWZyb3c9Yyg0LDIpKQ0KbGFwcGx5KHpzY29yZW1vZGlmX2NvbHMsIGZ1bmN0aW9uKGNvbCkgYm94cGxvdChkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWFuYVssY29sXSx4bGFiPWNvbCwgaG9yaXpvbnRhbCA9IFQpKQ0KDQpgYGANCg0KDQojIyMjIFJldmlzacOzbiBWYXJpYWJsZSBgSW5zdHJ1bWVudGFsbmVzc2ANCmBgYHtyfQ0KaW5zdHJ1bWVudGFsbmVzcyA8LSBjKCJpbnN0cnVtZW50YWxuZXNzIiwgInpzY29yZW1vZGlmX2luc3RydW1lbnRhbG5lc3MiKSANCg0KeCA8LSBkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzDQoNCm5faW50ZXJ2IDwtIDEwDQoNCg0KaW50ZXJ2YWxvcyA8LSByb3VuZChzZXEoMCxtYXgoeCksYnk9KG1heCh4KS1taW4oeCkpL25faW50ZXJ2KSwyKQ0KDQpsYWJzIDwtIGMoKQ0KZm9yIChpIGluIDE6bl9pbnRlcnYpew0KbGFiIDwtIHBhc3RlKGludGVydmFsb3NbaV0saW50ZXJ2YWxvc1tpKzFdLCBzZXA9J1xuJykNCmxhYnMgPC0gYXBwZW5kKGxhYnMsIGxhYikNCiAgICANCn0NCg0KYmlucyA8LSBjdXQoeCwgbl9pbnRlcnYsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSwgbGFiZWxzID0gbGFicykNCg0KYmFycGxvdCh0YWJsZShiaW5zKSkNCg0KYGBgDQoNCkhhY2Vtb3MgSy1tZWFucyBwYXJhIHBvZGVyIGRpc2NyZXRpemFyIGxhIHZhcmlhYmxlLiANCg0KYGBge3J9DQpzc2UgPC0gYygpDQpmb3IgKGsgaW4gMjo2KXsNCiAgY2x1c3RlcnMgPC0ga21lYW5zKGRmX2F1ZGlvX2ZlYXR1cmVzJGluc3RydW1lbnRhbG5lc3MsY2VudGVycyA9IGssIGl0ZXIubWF4ID0gMTAsIG5zdGFydCA9IGspDQogIHNzZSA8LSBhcHBlbmQoc3NlLCBjbHVzdGVycyR0b3Qud2l0aGluc3MpDQp9DQoNCnBsb3QoMjo2LHNzZSwgdHlwZSA9ICdsJywgeGxhYj0nQ2FudGlkYWQgZGUgQ2x1c3RlcnMnLCB5bGFiPSdTdW1hIEVycm9yIEN1YWRyw6F0aWNvJykNCg0KI2s9MyANCmNsdXN0ZXJzMyA8LSBrbWVhbnMoZGZfYXVkaW9fZmVhdHVyZXMkaW5zdHJ1bWVudGFsbmVzcyxjZW50ZXJzID0gMywgaXRlci5tYXggPSAxMCwgbnN0YXJ0ID0gMykNCg0KZGZfYXVkaW9fZmVhdHVyZXMkY2x1c3RlcnMgPC0gZmFjdG9yKGNsdXN0ZXJzMyRjbHVzdGVyKQ0KDQpsZXYgPC0gbGV2ZWxzKGRmX2F1ZGlvX2ZlYXR1cmVzJGNsdXN0ZXJzKQ0KDQpsYWJzIDwtIGMoKQ0KZm9yIChpIGluIGxldil7DQogIG1pbiA8LSBtaW4oZGZfYXVkaW9fZmVhdHVyZXMkaW5zdHJ1bWVudGFsbmVzc1tkZl9hdWRpb19mZWF0dXJlcyRjbHVzdGVycz09aV0pDQogIG1heCA8LSBtYXgoZGZfYXVkaW9fZmVhdHVyZXMkaW5zdHJ1bWVudGFsbmVzc1tkZl9hdWRpb19mZWF0dXJlcyRjbHVzdGVycz09aV0pDQogIGxhYiA8LSBwYXN0ZShtaW4sbWF4LCBzZXA9JyAtICcpDQogIGxhYnMgPC0gYXBwZW5kKGxhYnMsIGxhYikNCn0NCg0KbGFicw0KDQojIGJhcnBsb3QodGFibGUoZmFjdG9yKGNsdXN0ZXJzMyRjbHVzdGVyKSksIGxhYmVscyA9IGxhYnMpDQoNCg0KDQpgYGANCg0KDQojIFByZWd1bnRhcyBkZSBpbnZlc3RpZ2FjaW9uDQoNCiMjIFBhdHJvbiBDb211biBDYW5jaW9uZXMgZGVsIENoYXJ0DQrCv1F1w6kgY2FyYWN0ZXLDrXN0aWNhcyB0aWVuZW4gbGFzIGNhbmNpb25lcyBxdWUgZXN0w6FuIGVuIGVsIGNoYXJ0PyDCv0N1YWwgZXMgZWwgcGF0csOzbiBjb211biBxdWUgdGllbmVuIGxhcyBjYW5jaW9uZXMgbcOhcyBlc2N1Y2hhZGFzPyAodmVyIGRpc3BlcnNpb25lcywgbWVkaWEsIGdyYWZpY28gY29tcGFyYXRpdm8pDQpgYGB7cn0NCg0KDQojZnVuY2lvbiBwYXJhIGVzY2FsYXIgdmFyaWFibGUNCnNjYWxlX3ZibGUgPC0gZnVuY3Rpb24oeCl7DQogICh4IC0gbWVhbih4LCBuYS5ybSA9IFQpKS9zZCh4LCBuYS5ybSA9IFQpDQp9DQoNCmBgYA0KYGBge3J9DQojYW50aV9qb2luDQphbnRpX2pvaW5fYXVkaW9fY2hhcnRzIDwtIGRmX2F1ZGlvX2ZlYXR1cmVzICU+JSANCiAgc2VsZWN0KCJhcnRpc3RfbmFtZSIsImFydGlzdF9hbGwiLCAiYXJ0aXN0X2tleSIsDQogICAgICAgICAidHJhY2tfbmFtZSIsICJleHRlcm5hbF91cmxzX3Nwb3RpZnkiLCAiYWxidW1fbmFtZSIsICJhbGJ1bV9yZWxlYXNlX3llYXIiLA0KICAgICAgICAgYWxsX29mKGZlYXR1cmVzX2NvbnRpbnVhcyksIGFsbF9vZihmZWF0dXJlc19jYXRlZ29yaWNhcykpICU+JSANCiAgYW50aV9qb2luKCBkZl9jaGFydHMgJT4lDQogICAgICAgICAgICAgICBzZWxlY3QoICJUcmFja19OYW1lIiwgIkFydGlzdCIsICJVUkwiKSwNCiAgICAgICAgICAgICAgIGJ5ID0gYygiZXh0ZXJuYWxfdXJsc19zcG90aWZ5IiA9IlVSTCIsDQogICAgICAgICAgICAgICAgICAgICAgImFydGlzdF9rZXkiID0iQXJ0aXN0IiAgKSkNCiAgICAgICAgICAgICAgICMgYnkgPSBjKCJ0cmFja19uYW1lIiA9ICJUcmFja19OYW1lIikpDQoNCg0KYW50aV9qb2luX2F1ZGlvX2NoYXJ0c19jb21wbGV0ZSA8LSBuYS5vbWl0KGFudGlfam9pbl9hdWRpb19jaGFydHMpDQphbnRpX2pvaW5fYXVkaW9fY2hhcnRzX2NvbXBsZXRlX3NjYWxlIDwtIGFudGlfam9pbl9hdWRpb19jaGFydHNfY29tcGxldGUgJT4lIA0KICBkaXN0aW5jdCgpICU+JSANCiAgc2VsZWN0KGZlYXR1cmVzX2NvbnRpbnVhcykgICU+JSANCiAgbXV0YXRlX2FsbChzY2FsZV92YmxlKQ0KbnJvdyhhbnRpX2pvaW5fYXVkaW9fY2hhcnRzX2NvbXBsZXRlX3NjYWxlKQ0KDQpgYGANCg0KIyMgUXXDqSB0ZW1hcyBwZXJkdXJhbiBtdWNobyBlbiBlbCByYW5raW5nDQoNCiMjIyBBcnRpc3RhcyBxdWUgbWFzIGFwYXJlY2VuIGVuIGVsIGNoYXJ0DQpgYGB7cn0NCmpvaW5fYXVkaW9fY2hhcnRzICU+JSANCiAgZ3JvdXBfYnkoYXJ0aXN0X25hbWUpICU+JSANCiAgZHBseXI6OnN1bW1hcmlzZShuID0gbigpKSAlPiUgDQogIGFycmFuZ2UoLW4pDQpgYGANCg0KIyMjIFRyYWNrcyBxdWUgbWFzIGFwYXJlY2VuIGVuIGVsIGNoYXJ0DQpgYGB7cn0NCmpvaW5fYXVkaW9fY2hhcnRzICU+JSANCiAgZ3JvdXBfYnkodHJhY2tfbmFtZSwgYXJ0aXN0X25hbWUsZXh0ZXJuYWxfdXJsc19zcG90aWZ5KSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpc2UobiA9IG4oKSkgJT4lIA0KICBhcnJhbmdlKC1uKSAlPiUgDQogIHNlbGVjdCh0cmFja19uYW1lLCBuLCBldmVyeXRoaW5nKC4pKQ0KDQpgYGANCg0KDQojIMK/Q3XDoW50byB0aWVtcG8gZXN0w6FuIGVuIHVuIGNoYXJ0PyANCg0KYGBge3J9DQojIGNhbnRpZGFkIGRlIHNlbWFuYXMgcXVlIGVzdHV2aWVyb24gZW4gZWwgY2hhcnQNCg0KZGZfY2hhcnRzICU+JSANCiAgbXV0YXRlKHdlZWtfc3RhcnQ9YXMuRGF0ZSh3ZWVrX3N0YXJ0KSwNCiAgICAgICAgIHdlZWtfZW5kID0gYXMuRGF0ZSh3ZWVrX2VuZCksDQogICAgICAgICB3ZWVrX3llYXIgPSAoeWVhcih3ZWVrX3N0YXJ0KSkpICU+JQ0KICBhcnJhbmdlKEFydGlzdCwgVHJhY2tfTmFtZSkgJT4lIA0KICBncm91cF9ieShBcnRpc3QsIFRyYWNrX05hbWUsIFVSTCkgJT4lIA0KIGRwbHlyOjogc3VtbWFyaXNlKCBkYXlfaW4gPSBtaW4od2Vla19zdGFydCksDQogICAgICAgICAgICAgeWVhcl9pbiA9IHllYXIoZGF5X2luKSwNCiAgICAgICAgICAgICBkYXlfbWF4ID0gbWF4KHdlZWtfZW5kKSwNCiAgICAgICAgICAgICB5ZWFyX21heCA9IHllYXIoZGF5X21heCksDQogICAgICAgICAgICAgZHVyYWNpb25fY2hhcnRfZGlhcyA9IGRheV9tYXgtZGF5X2luLA0KICAgICAgICAgICAgIGR1cmFjaW9uX2NoYXJ0X2FuaW8gPSB5ZWFyX21heCAtIHllYXJfaW4pICU+JSANCiAgYXJyYW5nZShBcnRpc3QpDQoNCmBgYA0KDQojcHJ1ZWJhIGlnYWwgZGUgdHJhbnNmb3JtYWNpb24geSB0ZXN0IGRlIG5vcm1hbGlkYWQNCmBgYHtyfQ0Kam9pbl9hdWRpb19jaGFydHNbMTo1LCJhY291c3RpY25lc3MiXV4yDQoNCmxpYnJhcnkobm9ydGVzdCkNCg0KbG9nMTAoZGZfY2hhcnRfd19seXJpY3MkYWNvdXN0aWNuZXNzKQ0KDQpmb3IgKGkgaW4gZmVhdHVyZXNfY29udGludWFzKXsNCiAgIHggPC0gbG9nMTAoZGZfY2hhcnRfd19seXJpY3NbLGldKQ0KICAgeCA8LSBzaGFwaXJvLnRlc3QoeCkNCiAgIHogPC0geCRwLnZhbHVlDQogIHByaW50KHopDQogIH0NCg0KDQpgYGANCg0KDQoNCg0K